﻿using System;
using System.Drawing; // 确保项目引用 System.Drawing.Common (NuGet) 或使用如 ImageSharp
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public class NamedPipeImageReceiver : IDisposable
{
    private NamedPipeClientStream pipeClient;
    private readonly string targetPipeName; // 管道的基本名称
    private readonly string targetServerName; // 服务器名称, "." 代表本机
    private bool isDisposed = false;

    // 属性：检查客户端是否已连接
    public bool IsConnected => pipeClient?.IsConnected ?? false;

    // 构造函数
    // 参数: pipeName - 要连接的管道名称 (不含 \\.\pipe\ 前缀)
    // 参数: serverName - 管道服务器所在的机器名, "." 代表本机
    public NamedPipeImageReceiver(string pipeName, string serverName = ".")
    {
        // 确保控制台能良好显示中文 (主要用于此类内的日志)
        if (Console.IsOutputRedirected == false) // 避免在非控制台环境（如某些GUI日志）中设置
        {
            try { Console.OutputEncoding = Encoding.UTF8; } catch { /*忽略在受限环境中的错误*/ }
        }

        this.targetPipeName = pipeName;
        this.targetServerName = serverName;
    }

    // 方法：异步连接到命名管道服务器
    // 参数: timeoutMilliseconds - 连接超时时间 (毫秒)
    // 返回: Task<bool> - 异步操作的结果，true表示连接成功
    // 对应GUI操作："连接到服务器"按钮
    public async Task<bool> ConnectAsync(int timeoutMilliseconds = 5000)
    {
        if (IsConnected)
        {
            Console.WriteLine("C# 接收端：已连接到服务器。无需重复连接。");
            return true;
        }

        // 如果之前的实例存在但未连接或已断开，先清理
        if (pipeClient != null)
        {
            pipeClient.Dispose();
            pipeClient = null;
        }

        pipeClient = new NamedPipeClientStream(
            this.targetServerName,
            this.targetPipeName,
            PipeDirection.In, // 只接收数据
            PipeOptions.Asynchronous); // 明确使用异步选项

        Console.WriteLine($"C# 接收端：正在尝试连接到服务器 '{this.targetServerName}' 上的管道 '{this.targetPipeName}'...");
        try
        {
            // 使用 CancellationTokenSource 实现超时
            using (CancellationTokenSource cts = new CancellationTokenSource(timeoutMilliseconds))
            {
                await pipeClient.ConnectAsync(cts.Token);
            }

            // 如果 ConnectAsync 没有抛出异常，则表示连接成功
            Console.WriteLine("C# 接收端：已成功连接到管道。");
            return true;
        }
        catch (TimeoutException)
        {
            Console.WriteLine("C# 接收端：连接超时。");
            pipeClient?.Dispose();
            pipeClient = null;
            return false;
        }
        catch (IOException ex)
        {
            Console.WriteLine($"C# 接收端：连接期间发生IO异常：{ex.Message} (服务器可能未运行或管道名错误)");
            pipeClient?.Dispose();
            pipeClient = null;
            return false;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"C# 接收端：连接时发生未知错误：{ex.Message}");
            pipeClient?.Dispose();
            pipeClient = null;
            return false;
        }
    }

    // 方法：异步接收图像数据
    // 返回: Task<Image> - 异步操作的结果，成功则为 Image 对象，失败则为 null
    // 对应GUI操作："接收图像"按钮
    public async Task<Image> ReceiveImageAsync()
    {
        if (!IsConnected)
        {
            Console.WriteLine("C# 接收端：未连接到服务器，无法接收图像。");
            return null;
        }

        try
        {
            // 1. 读取图像大小 (uint32_t -> 4 字节)
            byte[] sizeBuffer = new byte[4];
            Console.WriteLine("C# 接收端：正在读取图像大小...");
            int bytesReadForSize = await ReadExactlyAsync(pipeClient, sizeBuffer, 0, 4);

            if (bytesReadForSize != 4)
            {
                Console.WriteLine($"C# 接收端：读取图像大小失败。期望 4 字节，实际读取 {bytesReadForSize} 字节。连接可能已断开。");
                Disconnect(); // 断开可能已损坏的连接
                return null;
            }

            uint imageSize = BitConverter.ToUInt32(sizeBuffer, 0);
            Console.WriteLine($"C# 接收端：期望图像大小：{imageSize} 字节。");

            // 对图像大小进行合理性检查
            if (imageSize == 0 || imageSize > 150 * 1024 * 1024) // 例如，限制最大150MB
            {
                Console.WriteLine($"C# 接收端：无效或过大的图像大小：{imageSize}。正在中止操作。");
                Disconnect();
                return null;
            }

            // 2. 读取图像数据
            byte[] imageData = new byte[imageSize];
            Console.WriteLine("C# 接收端：正在读取图像数据...");
            int totalBytesRead = await ReadExactlyAsync(pipeClient, imageData, 0, (int)imageSize);

            if (totalBytesRead == imageSize)
            {
                Console.WriteLine("C# 接收端：图像数据接收成功。");
                using (MemoryStream ms = new MemoryStream(imageData))
                {
                    // 注意: System.Drawing.Common 在 .NET Core/5+ 的非Windows平台上可能存在问题。
                    // 考虑使用 SixLabors.ImageSharp 等跨平台库。
                    Image receivedImage = Image.FromStream(ms);
                    Console.WriteLine($"C# 接收端：图像对象已从字节流创建。");
                    return receivedImage; // 成功返回图像对象
                }
            }
            else
            {
                Console.WriteLine($"C# 接收端：未能读取完整的图像数据。期望 {imageSize} 字节，实际读取 {totalBytesRead} 字节。");
                Disconnect();
                return null;
            }
        }
        catch (IOException ex) // 通常表示管道已由服务器关闭或网络中断
        {
            Console.WriteLine($"C# 接收端：读取数据时发生IO异常：{ex.Message}。服务器可能已关闭连接。");
            Disconnect(); // 确保客户端状态也更新
            return null;
        }
        catch (ObjectDisposedException ex) // 如果 pipeClient 在操作中途被 Dispose
        {
            Console.WriteLine($"C# 接收端：管道对象已被释放：{ex.Message}");
            // pipeClient 已经是 null 或已被 Dispose，无需再次调用 Disconnect()
            return null;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"C# 接收端：接收图像时发生未知错误：{ex.Message}");
            // 发生未知错误时，也考虑断开连接以清理状态
            // Disconnect(); 
            return null;
        }
    }

    // 辅助方法：确保从流中准确读取指定数量的字节
    private async Task<int> ReadExactlyAsync(Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)
    {
        int totalBytesRead = 0;
        while (totalBytesRead < count)
        {
            cancellationToken.ThrowIfCancellationRequested(); // 检查是否请求取消
            int bytesRead = await stream.ReadAsync(buffer, offset + totalBytesRead, count - totalBytesRead, cancellationToken);
            if (bytesRead == 0) // 流意外结束 (例如，服务器关闭了连接)
            {
                break; // 退出循环，totalBytesRead 将小于 count
            }
            totalBytesRead += bytesRead;
        }
        return totalBytesRead;
    }

    // 方法：断开与服务器的连接并释放资源
    // 对应GUI操作："断开连接"按钮
    public void Disconnect()
    {
        if (pipeClient != null)
        {
            Console.WriteLine("C# 接收端：正在断开与服务器的连接...");
            try
            {
                // NamedPipeClientStream 的 Dispose() 方法会负责关闭管道。
                pipeClient.Dispose();
            }
            catch (Exception ex)
            {
                // 捕获 Dispose 时可能发生的异常，尽管不常见
                Console.WriteLine($"C# 接收端：断开连接并释放资源时发生错误：{ex.Message}");
            }
            finally
            {
                pipeClient = null; // 确保引用置空
                Console.WriteLine("C# 接收端：已断开连接。");
            }
        }
        else
        {
            Console.WriteLine("C# 接收端：当前未连接，无需断开。");
        }
    }

    // 实现 IDisposable 接口
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // 通知垃圾回收器不需要再调用终结器
    }

    protected virtual void Dispose(bool disposing)
    {
        if (isDisposed) return;

        if (disposing)
        {
            // 释放托管资源 (如 pipeClient)
            Disconnect();
        }
        // 此处可以释放非托管资源 (如果此类直接持有的话)
        isDisposed = true;
    }

    // 终结器 (析构函数语法)，仅在 Dispose(bool) 未被调用时执行
    ~NamedPipeImageReceiver()
    {
        Dispose(false);
    }
}